/* 
 *  include/asm/i386.h
 * 
 *  last modified 2007.07.14 21:53 by ChenJie
 *
 */

#ifndef I386_H
#define I386_H

#include <clinux/config.h>

// DA意为描述符属性
#define DA_32		0x4000	//32位段
#define DA_LIMIT_4K	0x8000	//段界限粒度为4KB
#define DA_LIMIT_1B  0x0000   //段界限粒度为1B

#define DA_DPL0		0x00	// DPL = 0
#define DA_DPL1		0x20	// DPL = 1
#define DA_DPL2		0x40	// DPL = 2
#define DA_DPL3		0x60	// DPL = 3

#define DA_DR		0x90	//存在的只读数据段
#define DA_DRW		0x92	//存在的可读写数据段
#define DA_DRWA		0x93	//存在的已访问可读写数据段
#define DA_C		0x98	//存在的只执行代码段
#define DA_CR		0x9A	//存在的可执行可读代码段

#define DA_LDT		0x82	//局部描述符表段
#define DA_TaskGate	0x85	//任务门
#define DA_386TSS	0x89	//任务状态段
#define DA_386CGate	0x8C	//386调用门
#define DA_386IGate	0x8E	//386中断门
#define DA_386TGate	0x8F	//386陷阱门

// SA意为选择子属性
#define SA_RPL0		0x00	// RPL = 0	
#define SA_RPL1		0x01	// RPL = 1
#define SA_RPL2		0x02	// RPL = 2
#define SA_RPL3		0x03	// RPL = 3

#define SA_TIG		0x00	// 全局选择子
#define SA_TIL		0x04	// 局部选择子

// 分页机制常量说明
#define PG_P		0x01	// 页存在属性位
#define PG_RWR		0x00	// R/W属性位，读/执行
#define PG_RWW		0x02	// R/W属性位，读/写/执行
#define PG_USS		0x00	// U/S属性位，系统级
#define PG_USU		0x04	// U/S属性位，用户级

// 描述符说明
#define Descriptor(Base, Limit, Attr) \
	.word Limit & 0xffff; \
	.word Base & 0xffff; \
	.byte (Base >> 16) & 0x00ff; \
	.word ((Limit >> 8) & 0x0f00) | (Attr & 0xf0ff);\
	.byte (Base >> 24) & 0x00ff;

//内核代码段选择子：
#define KERNEL_CS	((1<<3)+(SA_TIG|SA_RPL0))
//内核数据段选择子：
#define KERNEL_DS	((2<<3)+(SA_TIG|SA_RPL0))
//内核堆栈段选择子：
#define KERNEL_SS	((2<<3)+(SA_TIG|SA_RPL0))

//用户态代码段选择子：
#define USER_CS		((1<<3)+(SA_TIL|SA_RPL3))
//用户态数据段选择子：
#define USER_DS		((2<<3)+(SA_TIL|SA_RPL3))
//用户态堆栈段选择子：
#define USER_SS		((3<<3)+(SA_TIL|SA_RPL3))


#define _set_gate(gate_addr, type, dpl, addr) __asm__(\
		"\tmovw %%dx, %%ax\n"\
		"\tmovw %0, %%dx\n"\
		"\tmovl %%eax, %1\n"\
		"\tmovl %%edx, %2\n"\
		::"i"((unsigned short)(0x8000 | (dpl<<13) | (type<<8))),\
		"o"(*((char*)(gate_addr))),\
		"o"(*(4+(char*)(gate_addr))),\
		"d"((char*)(addr)),\
		"a"(KERNEL_CS << 16) \
		)
		
// 设置陷阱门，要求特权极0, 不影响IF位。
// 主要用于CPU内部异常。		
#define set_trap_gate(idtn_addr, func) \
		_set_gate(idtn_addr, DA_386TGate, 0, func)
		
// 设置中断门，要求特权极0, 影响IF位（将IF复位为0，即不充许再次中断）
// 主要用于CPU外部硬件中断,但要注意IF只影响外部硬件中断，而不影响非屏蔽中断、
// 软件中断和内部中断与异常。
#define set_intr_gate(idtn_addr, func) \
		_set_gate(idtn_addr, DA_386IGate, 0, func)

// 设置系统调用门，要求特权极3, 不影响IF位
// 用户态时请求系统调用。
#define set_system_gate(idtn_addr, func) \
		_set_gate(idtn_addr, DA_386TGate, 3, func)

#define _set_seg_desc(desc_addr, type, base, limit) __asm__(\
		"\tmovw %%dx, (%%ebx)\n"\
		"\tmovw %%ax, 2(%%ebx)\n\t"\
		"\trorl $16, %%eax\n"\
		"\tmovb %%al, 4(%%ebx)\n"\
		"\tmovb %%cl, 5(%%ebx)\n"\
		"\trorl $16, %%edx\n"\
		"\torw  $0xC0, %%dx\n"\
		"\tmovb %%dl, 6(%%ebx)\n"\
		"\tmovb %%ah, 7(%%ebx)\n"\
		::"a"((unsigned long)base), \
		"c"((unsigned char)type),\
	       	"d"((unsigned long)limit),\
		"b"((unsigned long)desc_addr)\
		)

#define set_seg_desc(desc_addr, type, base, limit) \
	_set_seg_desc(((desc_addr)), ((type)), (base), (limit))

#define set_base(addr, base) __asm__(\
		"\tmovw %%dx, 2(%%eax)\n"\
		"\trorl $16, %%edx\n"\
		"\tmovb %%dl, 4(%%eax)\n"\
		"\tmovb %%dh, 7(%%eax)\n"\
		::"a"((char*)(addr)), "d"(base)\
		)

#define set_limit(addr, limit) __asm__(\
		"\tmovw %%dx, (%%eax)\n"\
		"\trorl $16, %%edx\n"\
		"\tmovb 6(%%eax), %%dh\n"\
		"\tandb $0xf0, %%dh\n"\
		"\torb  %%dh, %%dl\n"\
		"\tmovb %%dl, 6(%%eax)"\
		::"a"((char*)(addr)), "d"((limit-1)>>12)\
		)
#define get_base(addr) ({\
		unsigned long base;\
		__asm__ volatile(\
			"\tmovb 7(%%eax), %%dh\n"\
			"\tmovb 4(%%eax), %%dl\n"\
			"\tshll $16, %%edx\n"\
			"\tmovw 2(%%eax), %%dx"\
			:"=d"(base)\
			:"a"((char*)(addr))\
			);\
		base;})

#define get_limit(segment) ({\
		unsigned long limit;\
		__asm__(\
			"\tlsll %%ebx, %%eax\n"\
			"\tincl %%eax\n"\
			:"=a"(limit)\
			:"b"(segment);\
		       )\
		limit;})


//在全局表中填入ldt,tss的描述符：
#define _set_tssldt_desc(desc_addr, tssldt_base, tssldt_limit,type) __asm__(\
		"\tmovw %%dx, (%%ebx)\n"\
		"\tmovw %%ax, 2(%%ebx)\n\t"\
		"\trorl $16, %%eax\n"\
		"\tmovb %%al, 4(%%ebx)\n"\
		"\tmovb %%cl, 5(%%ebx)\n"\
		"\tmovb $0x00, 6(%%ebx)\n"\
		"\tmovb %%ah, 7(%%ebx)\n"\
		"\trorl $16, %%eax\n"\
		:\
		:"a"(tssldt_base), "b"(desc_addr), "c"(type), "d"(tssldt_limit)\
		)
			

// 设置gdt[n]指向地址为ldt_addr的ldt
#define set_ldt_desc(desc_addr, ldt_addr) \
	_set_tssldt_desc((desc_addr), (ldt_addr), 32,DA_LDT)

// 设置gdt[n]指向地址为tss_ldt的tss
#define set_tss_desc(desc_addr, tss_addr) \
	_set_tssldt_desc((desc_addr), (tss_addr), 104,DA_386TSS)

//加载TSS选择子：
#define ltr(selector)	__asm__("ltr  %%ax"::"a"(selector))
//加载LDT选择子：
#define lldt(selector)	__asm__("lldt %%ax"::"a"(selector))

#define outb(value, port) \
	__asm__("outb %%al, %%dx"::"a"(value),"d"(port))

#define inb(port)  ({\
		unsigned char value;\
		__asm__ volatile("inb %%dx, %%al":"=a"(value):"d"(port));\
		value;\
		})

#define lcr3(addr)  __asm__("movl %%eax, %%cr3"::"a"(addr))

// 获取page_fault时的CR2中的线性地址:
#define getCR2() ({\
		unsigned long addr; \
		__asm__ volatile("movl %%cr2, %%eax":"=a"(addr):); \
		addr; \
		})
/*
 * 禁止中断
 */
#define cli()	__asm__ __volatile__("cli"::)
/*
 * 允许中断
 */
#define sti()   __asm__ __volatile__("sti"::)
/*
 * 空指令
 */
#define nop()	__asm__ __volatile__("nop"::)
/*
 * 停机指令
 */
#define hlt()	__asm__ __volatile__("hlt"::)


//从ring0-->ring3:
//USER_CS: 0x0f
//USER_DS: 0x17
//USER_SS: 0x1f
//USER_ESP: 直接继承原esp, 因为原esp指向的就是userStack.
/*
 * 移动到用户态运行时, 运行状态主要发生如下变化:
 * 	1.使用USER_EIP, USER_CS.
 * 	2.使用USER_ESP, USER_SS.
 * 	3.ring0--->ring3.
 *
 * 执行iret时的堆栈结构:
 * USER_SS
 * USER_ESP
 * EFLAGS
 * USER_CS
 * USER_EIP
 * ......
 */
#define move_to_user_mode() __asm__ __volatile__(\
		"\tmovl  %%esp, %%eax\n"\
		"\tpushl $0x1f\n"\
		"\tpushl %%eax\n"\
		"\tpushfl\n"\
		"\tpushl $0x0f\n"\
		"\tpushl $1f\n"\
		"\tiret\n"\
		"1:\t\n"\
		"\tmovw $0x17, %%ax\n"\
		"\tmovw %%ax, %%ds\n"\
		"\tmovw %%ax, %%es\n"\
		"\tmovw %%ax, %%fs\n"\
		::)


#endif //I386_H
